package com.baijiaoxi.utils.excel;

import com.baijiaoxi.common.model.base.CommonError;
import com.baijiaoxi.common.model.base.CoreException;
import com.baijiaoxi.common.model.excel.Column;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
@Data
public class ExcelExportHelper {
    Map<String, Integer> indexMap;
    Map<String, Column> columns;
    List<String> deepKeyList = new ArrayList<>();
    private HSSFWorkbook wb;
    private HSSFCellStyle headerStyle;
    private HSSFCellStyle titleStyle;
    private HSSFCellStyle cellStyle;
    private int defaultWidth = 20;//默认行宽
    private boolean shrinkToFit = false;//自适应大小（会缩放）
    private boolean wrapped = true;//自动换行
    private String sheetTitle;
    private String tableTitle;
    private int headerHeightInPoints = 20;
    private int titleHeightInPoints = 20;
    private boolean needColumnTitle = true;

    public ExcelExportHelper() {
        wb = new HSSFWorkbook();
        columns = new LinkedHashMap<>();
        indexMap = new HashMap<>();
        buildDefaultHeaderStyle();
        buildDefaultTitleStyle();
        buildDefaultCellStyle();
    }

    public ExcelExportHelper setColumns(List<Column> columnList) {
        columnList.stream().forEach(column -> {
            this.columns.put(column.getKey(), column);
            if (column.getKey().contains("."))
                deepKeyList.add(column.getKey());
            indexMap.put(column.getKey(), columns.size() - 1);
        });
        return this;
    }

    public static ExcelExportHelper build() {
        ExcelExportHelper excelExportHelper = new ExcelExportHelper();
        return excelExportHelper;
    }

    private void buildDefaultHeaderStyle() {
        headerStyle = wb.createCellStyle();
        headerStyle.setAlignment(HorizontalAlignment.CENTER);//水平居中
        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中
        //设置边框样式
        headerStyle.setBorderBottom(BorderStyle.THIN); //下边框
        headerStyle.setBorderLeft(BorderStyle.THIN);//左边框
        headerStyle.setBorderTop(BorderStyle.THIN);//上边框
        headerStyle.setBorderRight(BorderStyle.THIN);//右边框
        HSSFFont fontStyle = wb.createFont();
        fontStyle.setBold(true);
        headerStyle.setFont(fontStyle);
    }

    private void buildDefaultTitleStyle() {
        titleStyle = wb.createCellStyle();
        titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中
        HSSFFont fontStyle = wb.createFont();
        fontStyle.setBold(true);
        titleStyle.setFont(fontStyle);
        //设置边框样式
        titleStyle.setBorderBottom(BorderStyle.THIN); //下边框
        titleStyle.setBorderLeft(BorderStyle.THIN);//左边框
        titleStyle.setBorderTop(BorderStyle.THIN);//上边框
        titleStyle.setBorderRight(BorderStyle.THIN);//右边框

    }

    private void buildDefaultCellStyle() {
        cellStyle = wb.createCellStyle();
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中
        cellStyle.setShrinkToFit(shrinkToFit);//自适应大小
        cellStyle.setWrapText(wrapped);//默认采取内容中的\n换行 setWrapText自动换行
        //设置边框样式
        cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
        cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
        cellStyle.setBorderTop(BorderStyle.THIN);//上边框
        cellStyle.setBorderRight(BorderStyle.THIN);//右边框
    }

    public ExcelExportHelper setNeedColumnTitle(boolean needColumnTitle) {
        this.needColumnTitle = needColumnTitle;
        return this;
    }

    public ExcelExportHelper setDefaultWidth(int defaultWidth) {
        this.defaultWidth = defaultWidth;
        return this;
    }

    public ExcelExportHelper setShrinkToFit(boolean shrinkToFit) {
        this.shrinkToFit = shrinkToFit;
        return this;
    }

    public ExcelExportHelper setWrapped(boolean wrapped) {
        this.wrapped = wrapped;
        return this;
    }

    public ExcelExportHelper setSheetTitle(String sheetTitle) {
        this.sheetTitle = sheetTitle;
        return this;
    }

    public ExcelExportHelper setTableTitle(String tableTitle) {
        this.tableTitle = tableTitle;
        return this;
    }

    public ExcelExportHelper setHeaderHeightInPoints(int headerHeightInPoints) {
        this.headerHeightInPoints = headerHeightInPoints;
        return this;
    }

    public ExcelExportHelper setTitleHeightInPoints(int titleHeightInPoints) {
        this.titleHeightInPoints = titleHeightInPoints;
        return this;
    }

    public ExcelExportHelper addColumn(Column column) {
        this.columns.put(column.getKey(), column);
        if (column.getKey().contains("."))
            deepKeyList.add(column.getKey());
        indexMap.put(column.getKey(), columns.size() - 1);
        return this;
    }


    public ExcelExportHelper addColumn(String key, String title) {
        addColumn(new Column(key, title));
        return this;
    }


    public ExcelExportHelper addColumn(String key, String title, int width) {
        addColumn(new Column(key, title, width));
        return this;
    }

    public <T> ExcelExportHelper addColumn(String key, String title, Map<T, String> reflectMap) {
        addColumn(new Column(key, title, reflectMap));
        return this;
    }

    public <T> ExcelExportHelper addColumn(String key, String title, int width, Map<T, String> reflectMap) {
        addColumn(new Column(key, title, width, reflectMap));
        return this;
    }

    public <T> byte[] export(List<T> list) {
        return export(list, null);
    }

    public <T> byte[] export(List<T> list, OutputStream out) {

        int dataStartIndex = 0;

        //创建excel表
        HSSFSheet sheet = wb.createSheet(sheetTitle);
        sheet.setDefaultColumnWidth(defaultWidth);//设置默认行宽

        //创建表头
        if (tableTitle != null && !tableTitle.isEmpty()) {
            HSSFRow row = sheet.createRow(dataStartIndex);
            row.setHeightInPoints(headerHeightInPoints);//行高
            HSSFCell cell = row.createCell(0);
            cell.setCellValue(sheetTitle);
            cell.setCellStyle(headerStyle);
            if (columns.size() > 1)
                sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, (columns.size() - 1)));
            dataStartIndex += 1;
        }
        HSSFCell hc;
        if (needColumnTitle) {
            //创建标题
            HSSFRow rowTitle = sheet.createRow(dataStartIndex);
            rowTitle.setHeightInPoints(titleHeightInPoints);

            for (String key : columns.keySet()) {
                hc = rowTitle.createCell(indexMap.get(key));
                hc.setCellValue(columns.get(key).getTitle());
                hc.setCellStyle(titleStyle);
            }
            dataStartIndex += 1;
        }

        byte result[] = null;


        try {
            //创建表格数据
            Field[] fields = null;
            int i = dataStartIndex;

            for (Object obj : list) {
                HSSFRow rowBody = sheet.createRow(i);
                /*       rowBody.setHeightInPoints(20);*/
                //1.先处理直接导出成员的情况
                if (obj instanceof Map) {
                    Map map = (Map) obj;
                    for (String key : indexMap.keySet()) {
                        hc = rowBody.createCell(indexMap.get(key));//可以columns转list去实现，性能没有试过暂时不用
                        hc.setCellStyle(cellStyle);
                        setCellValue(map.get(key), null, hc, columns.get(key).getReflectMap());
                    }
                } else {
                    //获取成员变量
                    fields = obj.getClass().getDeclaredFields();

                    for (Field f : fields) {
                        f.setAccessible(true);
                        String fieldName = f.getName();

                        //设置单元格值
                        if (indexMap.containsKey(fieldName)) {
                            Object va = f.get(obj);
                            if (null == va) {
                                va = "";
                            }
                            hc = rowBody.createCell(indexMap.get(fieldName));//可以columns转list去实现，性能没有试过暂时不用
                            hc.setCellStyle(cellStyle);
                            setCellValue(va, f, hc, columns.get(fieldName).getReflectMap());
                        }
                    }
                }

                //2.处理子对象变量
                if (!deepKeyList.isEmpty()) {
                    for (String memberName : deepKeyList) {
                        hc = rowBody.createCell(indexMap.get(memberName));
                        hc.setCellStyle(cellStyle);
                        setDeepMember(memberName, fields, obj, hc, columns.get(memberName).getReflectMap());
                    }
                }

                i++;
            }
            if (out == null) {
                out = new ByteArrayOutputStream();
                wb.write(out);
                result = ((ByteArrayOutputStream) out).toByteArray();
            } else {
                wb.write(out);
                result = null;
            }
        } catch (Exception ex) {
            throw CoreException.create(CommonError.exportExcelFail, ex);
        } finally {
            try {
                if (null != out) {
                    out.close();
                }
            } catch (IOException ex) {
                throw CoreException.create(CommonError.closeOutAfterExportExcel, ex);
            } finally {
                try {
                    wb.close();
                } catch (IOException ex) {
                    throw CoreException.create(CommonError.closeSheetAfterExportExcel, ex);
                }
            }
        }

        return result;
    }

    private static boolean setDeepMember(String memberName, Field[] fields, Object item, HSSFCell hc, Map<String, String> reflectMap) throws IllegalArgumentException, IllegalAccessException {
        String[] memberNameArr = null;
        boolean go = memberName.contains(".");
        String nowMember;
        if (go) {
            memberNameArr = memberName.split("\\.", 2);
            nowMember = memberNameArr[0];
        } else
            nowMember = memberName;


        Object va;
        Field field = null;
        if (fields == null) {
            //map
            Map map = (Map) item;
            va = map.get(nowMember);
        } else {
            for (Field f : fields) {
                if (f.getName().equals(nowMember)) {
                    field = f;
                    break;
                }
            }
            if (field == null)
                throw CoreException.create(CommonError.getExportFieldValueFail, memberName);


            field.setAccessible(true);
            va = field.get(item);
        }
        if (null == va) {
            va = "";
        }
        if (go) {
            if (va.equals("")) {
                setCellValue(va, field, hc, reflectMap);
                return true;
            } else if (va instanceof Map) {
                return setDeepMember(memberNameArr[1], null, va, hc, reflectMap);
            } else
                return setDeepMember(memberNameArr[1], va.getClass().getDeclaredFields(), va, hc, reflectMap);
        } else {

            setCellValue(va, field, hc, reflectMap);
            return true;
        }

    }

    private static void setCellValue(Object va, Field field, HSSFCell hc, Map<String, String> reflectMap) {
        String cellValue;
        if (va instanceof String) {
            //处理字符串
            cellValue = (String) va;
            if (reflectMap != null) {
                //format
                cellValue = reflectMap.get(cellValue);
            }
        } else if (va instanceof Date) {
            //处理日期
            Annotation annotation = field.getAnnotation(JsonFormat.class);
            if (annotation != null && !va.equals("")) {
                JsonFormat jsonFormat = (JsonFormat) annotation;
                cellValue = new SimpleDateFormat(jsonFormat.pattern()).format(va);
            } else cellValue = "";
        } else if (field == null) {
            cellValue = va.toString();
        } else if (reflectMap != null) {
            //format
            cellValue = reflectMap.get(va.toString());
        } else cellValue = va.toString();
        hc.setCellValue(cellValue);
    }

}
